// src/components/ErrorBoundary.tsx

// 导入 React 核心库
import React from 'react';

// 从 Chakra UI 导入需要的组件
// Box: 基础布局容器
// Text: 文本组件
// Button: 按钮组件
import { Box, Text, Button } from '@chakra-ui/react';

/**
 * ErrorBoundary 组件的内部状态接口
 * 定义了组件可能处于的状态
 */
interface ErrorBoundaryState {
  hasError: boolean; // 标记是否捕获到错误
  error?: Error;     // 存储捕获到的具体错误对象（可选）
}

/**
 * ErrorBoundary：错误边界组件
 *
 * 用途：
 * - 捕获其子组件树中任何位置抛出的 JavaScript 错误
 * - 防止整个应用因一个组件崩溃而白屏
 * - 显示降级 UI（如“出错了”提示）
 * - 记录错误信息
 *
 * 注意：
 * - 必须是类组件（Class Component）
 * - 不能捕获自身抛出的错误
 * - 不能捕获异步错误（如 setTimeout、Promise.reject）
 * - 不能捕获服务端渲染错误或事件处理器中的错误
 */
export class ErrorBoundary extends React.Component<
  { children: React.ReactNode }, // 接收一个 children 属性，表示被包裹的子组件
  ErrorBoundaryState           // 组件的内部状态类型
> {
  /**
   * 构造函数
   * 初始化组件状态：默认没有错误
   */
  constructor(props: { children: React.ReactNode }) {
    super(props);
    this.state = { hasError: false }; // 初始状态：未发生错误
  }

  /**
   * 静态方法：在捕获到错误后，用于更新组件状态
   *
   * 执行时机：
   * - 渲染过程中子组件抛出错误时
   * - React 会调用此方法来决定新的 state
   *
   * @param error - 捕获到的错误对象
   * @returns 新的状态对象
   */
  static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    // 更新状态，标记为“有错误”，并保存错误对象
    return { hasError: true, error };
  }

  /**
   * 生命周期方法：在错误被捕获后执行（主要用于副作用，如日志上报）
   *
   * 与 getDerivedStateFromError 的区别：
   * - getDerivedStateFromError: 用于更新 state（纯函数，不能有副作用）
   * - componentDidCatch: 可以执行副作用（如发送错误日志）
   *
   * @param error - 捕获到的错误对象
   * @param errorInfo - 包含错误堆栈信息的对象，特别是 componentStack
   */
  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    // 在控制台打印错误信息，便于开发调试
    console.error('Error caught by boundary:', error, errorInfo);

    // 这里可以扩展：
    // - 发送错误日志到服务器（如 Sentry、LogRocket）
    // - 上报给监控系统
    // logErrorToService(error, errorInfo.componentStack);
  }

  /**
   * 渲染方法
   * 根据当前状态决定渲染内容
   */
  render() {
    // 如果捕获到错误，显示错误提示界面
    if (this.state.hasError) {
      return (
        <Box p={4} textAlign="center">
          {/* 主要提示文本 */}
          <Text fontSize="xl" mb={4}>应用出错了</Text>

          {/* 显示具体的错误信息 */}
          <Text fontSize="sm" mb={4} color="red.500">
            {this.state.error?.toString()} {/* 将错误对象转为字符串显示 */}
          </Text>

          {/* 重试按钮：点击后重置状态，重新尝试渲染子组件 */}
          <Button onClick={() => this.setState({ hasError: false })}>
            重试
          </Button>
        </Box>
      );
    }

    // 如果没有错误，正常渲染子组件
    return this.props.children;
  }
}
